#include "rna.h"

void cria_entrada_zerada_ponteiro(P_PESOS entrada){
    *entrada = calloc(1, sizeof(PESOS));
    //entrada bias
    entrada[0]->xs[0] = 1;
}

PESOS cria_entrada_zerada(){
    PESOS novo = calloc(1, sizeof(pesos));
    memset(novo->xs, 0,NUM_ENTRADAS*sizeof(float));
    //entrada bias
    novo->xs[0] = 1;
    return novo;
}

void cria_entrada_aleatoria_ponteiro(P_PESOS entrada){
    *entrada = calloc(1, sizeof(PESOS));
    register int i = 0;
    for( ; i < NUM_ENTRADAS; ++i)
        (*entrada)->xs[i] = ( i != 0 ? ( random() % 1000 ) / 1000.0 : 1 );
}

PESOS cria_entrada_aleatoria(){
    PESOS novo = calloc(1, sizeof(pesos));
    register int i = 0;
    for( ; i < NUM_ENTRADAS; ++i)
        novo->xs[i] = ( i != 0 ? ( random() % 1000 ) / 1000.0 : 1 );
    return novo;
}

int regra_delta(PESOS pesados, ENTER entrada){
    float sum = 0.0;
    register int i = 0;
    for ( ; i < NUM_ENTRADAS; ++i)
        sum += pesados->xs[i] * entrada->xs[i];
    return ( sum >= 0.0 ? 1 : 0 );
}

int regra_treinamento(enter *entrada, P_PESOS pesados, int num_neuronios, int num_padroes){
    int erros = 0;
    register int neuronio_atual, padrao_atual, peso_atual;
    //para cada neurônio na camada de saída
    for ( neuronio_atual = 0; neuronio_atual < num_neuronios; ++neuronio_atual){
        //para cada padrao na entrada
        for ( padrao_atual = 0; padrao_atual < num_padroes; ++padrao_atual){
            //calcular a saida obtida
            int obtido = regra_delta(pesados[neuronio_atual], &(entrada[padrao_atual]));
            //se a saida obtida nao for a desejada
            if ( obtido != respostas_padroes[neuronio_atual][padrao_atual] ){
                //atualizar todos os pesos (inclusive o do bias) de acordo com a regra de aprendizado do perceptron
                for ( peso_atual = 0; peso_atual < 31; ++peso_atual ){
                    pesados[neuronio_atual]->xs[peso_atual] += ( obtido == 1 ? (-1.0) * entrada[padrao_atual].xs[peso_atual] : (1.0) * entrada[padrao_atual].xs[peso_atual] );
                }
                //contabilizar erros
                erros++;
            }
        }
    }
    return erros;
}

void treinar(enter *entrada, P_PESOS pesados, int num_neuronios, int num_padroes){
    register int vezes = 0;
    //treinar a rede enquanto nao tiver erros na camada de saida, ou passar um determinado numero maximo de epocas de treinamento
    while( !( regra_treinamento(entrada, pesados, num_neuronios, num_padroes) == 0 ) && ( vezes < MAX_EPOCAS ) )
        vezes++;
    printf("Treinou por %d epocas\n",vezes);
}

